#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>


#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

//下面定义shell的全局数据
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
char cwd[1024];
char cwdenv[1024];
int lastcode=0;
//环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs=0;

//2.5关于重定向关心的内容
#define NONE_REDIR 0
#define INPUT_REFIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir = NONE_REDIR;
std::string filename;

//#define Trimspace(CMD,index) do{
//    while(isspace(CMD[index]))
//    {
//        index++;
//    }
//} while(0)




void Trimspace(char cmd[],int& end)
{
    while(isspace(cmd[end]))
    {
        end++;
    }
}


const char* GetUserName()
{
    const char*name = getenv("USER");
    return name==NULL?"None":name;
}

const char* GetHostName()
{
    const char*hostname = getenv("HOSTNAME");
    return hostname==NULL?"None":hostname;
}
//得到路径，注意不要用环境变量
const char* GetPwd()
{
    //const char*pwd = getpwd("PWD");
    const char*pwd = getcwd(cwd,sizeof(cwd));
    //修改环境变量
    if(pwd!=NULL)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return pwd==NULL?"None":pwd;
}

const char* GetHome()
{
    const char* home=getenv("HOME");
    return home==NULL?"":home;
}
//路径提取最后一个
std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir=pwd;
    if(dir==SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos) return "BUG?";
    return dir.substr(pos+1);
}

void MakeCommandLine(char cmd_prompt[],int size)
{
    snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str()); 
}
//打印命令行
void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}
//用户输入
bool GetCommandLine(char* out,int size)
{
    char *c=fgets(out,size,stdin);
    if(c==NULL) return false;
    out[strlen(out)-1]=0;//清除换行\n
    if(strlen(out)==0) return false;
    return true;
}
//命令行分割
bool CommandParse(char* commandline)
{
#define SEP " "
    g_argc=0;
    g_argv[g_argc++]=strtok(commandline,SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));//最后一个是NULL
    g_argc--;//NULL也会算上，这里把这个减去
    return g_argc>0?true:false;
}

void PrintArgv()
{
    for(int i=0;g_argv[i];i++)
    {
        printf("argv[%d]->%s\n",i,g_argv[i]);
    }
    printf("argc:%d\n",g_argc);
}
//两个内键命令，cd和echo
bool Echo()
{
    if(g_argc==2)
    {
        std::string opt=g_argv[1];
        if(opt=="$?")
        {
            std::cout<<lastcode<<std::endl;
            lastcode=0;
            return true;
        }
        if(opt[0]=='$')//第一个字符是$
        {
            std::string env_name=opt.substr(1);//去掉$的后面的内容
            const char* env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout<<env_value<<std::endl;
        }
    }
    return true;
}
bool Cd()
{
    if(g_argc==1)
    {
        std::string home=GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where=g_argv[1];
        if(where=="-")
        {
    
        }
        else if(where=="~")
        {
    
        }
        else
        {
            chdir(where.c_str());
        }
    }
    return true;
}
//检查内键命令
bool CheckAndExecBuiltin()
{
    std::string cmd=g_argv[0];
    if(cmd=="cd")
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        Echo();
        return true;
    }
    return false;
}
//命令命令执行
int Execute()
{

    pid_t id=fork();
    if(id==0)
    {
        int fd=-1;
        //子进程检查重定向
        if(redir==INPUT_REFIR)
        {
            fd=open(filename.c_str(),O_RDONLY);
            if(fd<0) exit(1);
            dup2(fd,0);
            close(fd);
        }
        else if(redir==OUTPUT_REDIR)
        {
            fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC,0666);
            if(fd<0) exit(1);
            dup2(fd,1);
            close(fd);
        }
        else if(redir==APPEND_REDIR)
        {    
            fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);
            if(fd<0) exit(1);
            dup2(fd,1);
            close(fd);
        }
        else
        {
            
        }
        //child
        execvp(g_argv[0],g_argv);
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
        lastcode=WEXITSTATUS(status);
    }
    return 0;
}

void InitEnv()
{
    extern char** environ;
    memset(g_env,0,sizeof(g_env));
    g_envs=0;
    //从配置文件夹里来
    //1.获取环境变量
    for(int i=0;environ[i];i++)
    {
        g_env[i]=(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs]=NULL;
    
    //2.导成环境变量
    for(int i=0;g_env[i];i++)
    {
        putenv(g_env[i]);
    }
    environ=g_env;

}

void RedirCheck(char commandline[])
{
    redir=NONE_REDIR;
    filename.clear();

    int start=0;
    int end=strlen(commandline)-1;
    while(end>start)
    {
        if(commandline[end]=='<')
        {
            commandline[end++]=0;
            Trimspace(commandline,end);
            redir=INPUT_REFIR;
            filename=commandline+end;
            break;
        }
        else if(commandline[end]=='>')
        {
            if(commandline[end-1]=='>')
            {
                //>>
                commandline[end-1]=0;
                redir=APPEND_REDIR;
            }
            else
            {
                //>
                redir=OUTPUT_REDIR;
            }
            commandline[end++]=0;
            Trimspace(commandline,end);
            filename=commandline+end;
            break;
        }
        else
        {
            end--;
        }
    }
}

int main()
{
    //shell启动时，从系统里获取环境变量
    //我们自己的环境变量信息应该从父shell里来
    InitEnv();
    while(true)
    {
        //1.输出命令行提示符
        PrintCommandPrompt();
        
        //2.获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline,sizeof(commandline)))
            continue;
        
        //2.5重定向分析
        RedirCheck(commandline);
        printf("redir:%d,filename:%s\n",redir,filename.c_str());

        //3.命令行分析
        CommandParse(commandline);
        //PrintArgv();
        
        //4.
        if(CheckAndExecBuiltin())
            continue;
        //5.执行命令
        Execute();
    }
    return 0;
}
